package com.wzw.basics.concurrent.collection;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Wangzhiwen
 */
public class CollectionSafe {

    public static void main(String[] args) {
        ListDemo.m1();
       // ListDemo.m2();
    }

}

class ListDemo {

    private static final List<String> list;

    private static final List<String> safeList;

    static {
        list = Stream.of(UUID.randomUUID().toString(),UUID.randomUUID().toString(),UUID.randomUUID().toString()).collect(Collectors.toList());
        safeList = new CopyOnWriteArrayList<>(list);
    }

    /**
     * 多线程对ArrayList集合修改,java.util.ConcurrentModificationException并发修改异常
     * 异常原因：
     *      ArrayList 内部有一个变量 modCount 来记录集合被修改的次数。
     *      假设线程A将通过迭代器next()获取下一元素时，从而将其打印出来。
     *      但之前，其他某线程添加新元素至list，结构发生了改变，modCount自增。
     *      当线程A运行到checkForComodification()，expectedModCount是modCount之前自增的值，
     *      判定modCount != expectedModCount为真，继而抛出ConcurrentModificationException。
     *      final void checkForComodification() {
     *         if (modCount != expectedModCount)
     *             throw new ConcurrentModificationException();
     *      }
     * 解决方案：
     *      1.使用线程安全的 Vector
     *      2.使用Collections创建同步集合
     *      3.可以使用读写集合 CopyOnWriteArrayList 解决.
     * 原理：
     *    采用读写分离的方案。可以并发读取，写入时是独占
     *    内置了
     *      final transient ReentrantLock lock = new ReentrantLock();
     *      private transient volatile Object[] array;
     *    添加元素的时候，不直接往当前容器Object[]添加，而是先将当前容器Object[]进行copy，
     *    复制出一个新的容器Object[] newElements，然后新的容器Object[ ] newELements里添加元素，
     *    添加完元素之后，再将原容器的引用指向新的容器setArray (newElements)。
     *    public boolean add(E e) {
     *         final ReentrantLock lock = this.lock;
     *         lock.lock();
     *         try {
     *             Object[] elements = getArray();
     *             int len = elements.length;
     *             Object[] newElements = Arrays.copyOf(elements, len + 1);
     *             newElements[len] = e;
     *             setArray(newElements);
     *             return true;
     *         } finally {
     *             lock.unlock();
     *         }
     *     }
     *  缺点：
     *    适用于读操作多写操作少的并发场景，因为每次add都是重新创建数组对象在指向原地址。会造成性能损耗
     */
    public static void m1(){
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            },"A").start();
        }
    }

    public static void m2(){
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                safeList.add(UUID.randomUUID().toString());
                System.out.println(safeList);
            },"A").start();
        }
    }
}

class SetDemo {

    /*
     * set集合也是线程不安全的
     * 解决方案：
     *     1.使用Collections同步方法创建set集合
     *     2.使用CopyOnWriteArraySet（推荐）
     * 原理：
     *      读写分离，内部聚合了一个CopyOnWriteArrayList集合
     */
}

class MapDemo {

    /*
     * HashMap 多线程下不安全
     * 解决方案：
     *      1.使用HashTable 效率低
     *      2.使用Collections创建同步集合
     *      3.使用ConcurrentHashMap（推荐）
     */

}
